#region Imported Namespaces
using System;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
using System.IO;
#endregion

namespace PHSRAG.Insight21.FormsManagement.Serialization
{
	#region Class Form
	/// <summary>
	/// Form class is a scaled down version of a Questionnaire that can be serialized.
	/// </summary>
	public class Form
	{
		#region Private instance fields
		private string name = string.Empty;
		private string answer = string.Empty;
		private bool remove = false;
		private bool allowMultiple;
		private string dateLastModified = string.Empty;
		private ArrayList formItems;
		#endregion

		#region Properties
		/// <summary>
		/// Gets or sets the name of the questionnaire
		/// </summary>
		[XmlAttribute]
		public string Name
		{
			get { return name; }
			set { name = value; }
		}

		/// <summary>
		/// Gets or sets the QuestionnaireAnswer name
		/// </summary>
		[XmlAttribute]
		public string Answer
		{
			get { return answer; }
			set { answer = value; }
		}

		[XmlAttribute]
		public bool Remove
		{
			get { return remove; }
			set { remove = value; }
		}

		/// <summary>
		/// Gets/Sets whether the questionnaire allows multiples
		/// </summary>
		[XmlAttribute]
		public bool AllowMultiple
		{
			get { return allowMultiple; }
			set { allowMultiple = value; }
		}

		/// <summary>
		/// Gets or sets the date on which the questionnaireanswer was last modified
		/// </summary>
		[XmlAttribute]
		public string DateLastModified
		{
			get { return dateLastModified; }
			set { dateLastModified = value; }
		}

		/// <summary>
		/// Gets or sets all the top-level questions (questions which have no parent)
		/// </summary>
		[XmlArrayItem(ElementName="FormItem", Type=typeof(FormItem))]
		[XmlArray(ElementName="FormItems")]
		public ArrayList FormItems
		{
			get { return formItems; }
			set { formItems = value; }
		}
		#endregion

		#region Constructors
		/// <summary>
		/// Default Constructor required while serializing the object
		/// </summary>
		public Form()
		{
			formItems = new ArrayList();
		}
		#endregion

		#region Public methods
		/// <summary>
		/// Gets the FormItem with the specified QuestionnaireItemName, ParentName, ParentValue. 
		/// Starts searching with the top level formitems and then calls SearchChildFormItems recursively until the formitem is found.
		/// </summary>
		/// <param name="parentQuestionName">The parent question name of the questionnaireitem being fetched</param>
		/// <param name="parentQuestionValue">The parent value for the questionnaireitem being fetched</param>
		/// <param name="questionName">The item name that needs to be returned</param>
		/// <returns>A FormItem object with the specified parentname, parentvalue and itemname</returns>
		public FormItem GetFormItem(string parentQuestionName, string parentQuestionValue, string questionName)
		{
			FormItem f = null;
			foreach(FormItem o in formItems)
			{
				f = SearchChildFormItems(o, parentQuestionName, parentQuestionValue, questionName);
				if (f!= null)
					break;
			}
			return f;
		}


		/// <summary>
		/// Generates the answer object hierarchy from the form and adds it to the questionnaire answer.
		/// </summary>
		/// <param name="cachePolicy">The prevalent cache policy</param>
		/// <param name="questionnaireAnswer">The questionnaire answer to which the answer hierarchy is to be added</param>
		/// <param name="isQAClone">
		/// Indicates whether or not the answer hierarchy is being generated for a clone of the QuestionnaireAnswer.
		/// If so, the FormAnswer's Id is ignored.
		/// </param>
		public void DeriveAnswers(Policy.ICachePolicy cachePolicy, QuestionnaireAnswer questionnaireAnswer, bool isQAClone)
		{
			if (questionnaireAnswer.Answers == null)
				questionnaireAnswer.Answers = new ArrayList();

			foreach (FormItem formItem in FormItems)
			{
				//top-level questions have no parent answers, hence 0 is passed in as the third parameter
				SaveAnswers(cachePolicy, formItem, questionnaireAnswer.Answers, 0, questionnaireAnswer.Id, isQAClone);
			}
		}
		#endregion

		#region Private methods
		/// <summary>
		/// Recursively loops through the FormItems collection of FormItem object to get the FormItem with the specified QuestionnaireItemName, ParentName, ParentValue. 
		/// For multiselect and dropdownmultiselect questions, loops through the children whose parentvalue matches the selected answer
		/// </summary>
		/// <param name="formItem">The FormItem which (and whose children) have to be matched against the rest of the three parameters</param>
		/// <param name="parentQuestionName">The ParentName of the FormItem being searched</param>
		/// <param name="parentQuestionValue">The ParentValue of the FormItem being searched</param>
		/// <param name="questionName">The name of the FormItem</param>
		/// <returns>The FormItem object that matches the specified ParentName, ParentValue, and Name</returns>
		private FormItem SearchChildFormItems(FormItem formItem, string parentQuestionName, string parentQuestionValue, string questionName)
		{
			FormItem f = null;
			if (formItem.Name == questionName && formItem.ParentName == parentQuestionName && formItem.ParentValue == parentQuestionValue)
				return formItem;
			//for multiselect, dropdownmultiselect and customvalueselector questions, only loop through the children whose parent value matches the selected answer
			if (formItem.Type == QuestionnaireItem.MultiSelectItemType 
				|| formItem.Type == QuestionnaireItem.DropDownMultiSelectItemType
				|| formItem.Type == QuestionnaireItem.CustomValueSelectorItemType)
			{
				foreach(FormAnswer answer in formItem.Answers)
				{
					if (answer.Selected)
					{
						foreach(FormItem o in formItem.FormItems)
						{
							if (o.ParentValue == answer.Answer)
							{
								f = SearchChildFormItems(o, parentQuestionName, parentQuestionValue, questionName);
								if (f!= null)
									break;
							}
							//no break here because there could be multiple child items with same parentvalue
						}
						break; //there can only be one answer that is selected
					}
				}
			}
			else
			{
				foreach(FormItem o in formItem.FormItems)
				{
					f = SearchChildFormItems(o, parentQuestionName, parentQuestionValue, questionName);
					if (f != null)
						return f;
				}
			}
			return f;
		}

		/// <summary>
		/// Updates the answers collection of the QuestionnnaireAnswer with the new values entered by the user. 
		/// For ease of mapping, when an answer is converted into FormAnswer, the answer id is also stored in FormAnswer. 
		/// For new answers provided by the user, the formanswer id would be zero. Based on the value of the formanswer id
		/// old values in the Answers collection of the QuestionnaireAnswer are updated or new Answer objects are created and
		/// added to the existing collection
		/// </summary>
		/// <param name="formItem">The serializable version of the QuestionnaireItem for which answers have to be saved</param>
		/// <param name="itemAnswers">Arraylist holding Answer objects</param>
		/// <param name="parentAnswerId">Parent Answer object's id</param>
		/// <param name="questionnaireAnswerId">QuestionnaireAnswer object's id</param>
		private void SaveAnswers(Policy.ICachePolicy cachePolicy, FormItem formItem, ArrayList itemAnswers, int parentAnswerId, int questionnaireAnswerId, bool isQAClone)
		{

			foreach (FormAnswer formAnswer in formItem.Answers)
			{
				Answer answer = null;
				if (formAnswer.Id != 0 && !isQAClone)
				{
					for (int i = itemAnswers.Count - 1; i >= 0; --i)
					{
						answer = (Answer)itemAnswers[i];
						if (formAnswer.Id == answer.Id)
						{
							if (!formAnswer.Deleted)
								answer.Value = formAnswer.Answer;
							else
								answer.MarkForDelete = true;
							break;
						}
					}
				}
				else
				{
					bool answerExists = false;

					//If a protocol is unlocked and the form is being modified, then the answers are cloned and id's are set
					//to zero. Hence, checking if answers with same name and value already exist.
					foreach (Answer ans in itemAnswers)
					{
						if (ans.Name == formItem.Name && ans.Value == formAnswer.Answer) //cloned answer
						{
							answerExists = true;
							answer = ans;
							if (formAnswer.Deleted)
								ans.MarkForDelete = true;
							else
								ans.MarkForDelete = false;
							break;
						}
					}
					if (!answerExists && !formAnswer.Deleted)
					{
						answer = new Answer(cachePolicy);
						answer.ParentAnswerId = parentAnswerId;
						answer.Value = formAnswer.Answer;
						answer.QuestionnaireAnswerId = questionnaireAnswerId;
						answer.Name = formItem.Name;
						itemAnswers.Add(answer);
					}
				}

				if (answer != null)
				{
					//Get the child form items that are dependant on the current answer for the current formitem 
					//and save its answers.
					foreach (FormItem childItem in formItem.FormItems)
					{
						if (childItem.ParentValue == formAnswer.Answer)
						{
							if (childItem.Answers != null && childItem.Answers.Count > 0)
							{
								if (answer.ChildAnswers == null)
									answer.ChildAnswers = new ArrayList();
								SaveAnswers(cachePolicy, childItem, answer.ChildAnswers, answer.Id, questionnaireAnswerId, isQAClone);
							}
						}
					}
				}
			}
		}
		#endregion
	}
	#endregion
}
